//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>

#include "lpc8712.h"
#include "ite8172.h"

static void LPCEnterMBPnP(void)
{
    int i;
    unsigned char key[4] = {0x87, 0x01, 0x55, 0x55};

    for (i = 0; i < 4; i++) {
        Outb(KSEG1ADDR(LPC_KEY_ADDR), key[i]);
    }
}

static void LPCExitMBPnP(void)
{
    Outb(KSEG1ADDR(LPC_KEY_ADDR), 0x02);
    Outb(KSEG1ADDR(LPC_DATA_ADDR), 0x02);
}

void LPCSetConfig(char LdnNumber, char Index, char data)
{
    LPCEnterMBPnP();                // Enter IT8712 MB PnP mode
    Outb(KSEG1ADDR(LPC_KEY_ADDR), 0x07);
    Outb(KSEG1ADDR(LPC_DATA_ADDR), LdnNumber);
    Outb(KSEG1ADDR(LPC_KEY_ADDR), Index);
    Outb(KSEG1ADDR(LPC_DATA_ADDR), data);
    LPCExitMBPnP();
}

char LPCGetConfig(char LdnNumber, char Index)
{
    char rtn;

    LPCEnterMBPnP();                // Enter IT8712 MB PnP mode
    Outb(KSEG1ADDR(LPC_KEY_ADDR), 0x07);
    Outb(KSEG1ADDR(LPC_DATA_ADDR), LdnNumber);
    Outb(KSEG1ADDR(LPC_KEY_ADDR), Index);
    rtn = Inb(KSEG1ADDR(LPC_DATA_ADDR));
    LPCExitMBPnP();
    return rtn;
}

int SearchIT8712(void)
{
    unsigned char Id1, Id2;
    unsigned short Id;

    LPCEnterMBPnP();
    Outb(KSEG1ADDR(LPC_KEY_ADDR), 0x20); /* chip id byte 1 */
    Id1 = Inb(KSEG1ADDR(LPC_DATA_ADDR));
    Outb(KSEG1ADDR(LPC_KEY_ADDR), 0x21); /* chip id byte 2 */
    Id2 = Inb(KSEG1ADDR(LPC_DATA_ADDR));
    Id = (Id1 << 8) | Id2;
    LPCExitMBPnP();
    if (Id == 0x8712)
        return TRUE;
    else
        return FALSE;
}

void InitLPCInterface(void)
{
    unsigned char bus, dev_fn;
    unsigned long data;

    bus = 0;
    dev_fn = 1<<3 | 4;

    /* pci cmd, SERR# Enable */
    Outl(KSEG1ADDR(IT_CONFADDR),
        (bus << IT_BUSNUM_SHF) | (dev_fn << IT_FUNCNUM_SHF)
        | ((0x4 / 4) << IT_REGNUM_SHF));
    data = Inl(KSEG1ADDR(IT_CONFDATA)) | 0x0100;

    Outl(KSEG1ADDR(IT_CONFADDR),
        (bus        << IT_BUSNUM_SHF)   |
         (dev_fn      << IT_FUNCNUM_SHF) |
         ((0x4 / 4) << IT_REGNUM_SHF));
    Outl(KSEG1ADDR(IT_CONFDATA), data);

    /* setup serial irq control register */
    Outl(KSEG1ADDR(IT_CONFADDR),
         (bus        << IT_BUSNUM_SHF)   |
         (dev_fn      << IT_FUNCNUM_SHF) |
         ((0x48 / 4) << IT_REGNUM_SHF));
    data = Inl(KSEG1ADDR(IT_CONFDATA));
    data  = (data & 0xffff00ff) | 0xc400;
    Outl(KSEG1ADDR(IT_CONFADDR),
         (bus        << IT_BUSNUM_SHF)   |
         (dev_fn      << IT_FUNCNUM_SHF) |
         ((0x48 / 4) << IT_REGNUM_SHF));
    Outl(KSEG1ADDR(IT_CONFDATA), data);

    /* Enable I/O Space Subtractive Decode */
    /* default 0x4C is 0x3f220000 */
    Outl(KSEG1ADDR(IT_CONFADDR),
         (bus        << IT_BUSNUM_SHF)   |
         (dev_fn      << IT_FUNCNUM_SHF) |
         ((0x4C / 4) << IT_REGNUM_SHF));
    Outl(KSEG1ADDR(IT_CONFDATA), 0x3f2200f3);
}
